<?php
/**
 * Třída reprezentující generátor playlistů
 *
 * @author Ladislav Kryštovský
 */
class Playlist_Generator_Party implements Playlist_GeneratorInterface
{
    const MAX_LIMIT = 100;
    const STANDARD_LIMIT = 10;
    const FREQ_LIMIT_FOR_ARTIST = 0.5;
    const FREQ_LIMIT_FOR_SONG = 0.5;
    const POINT_FOR_TOP_ARTIST = 1;
    const POINT_FOR_TOP_SONG = 1;
    const PERCENT_POINT_FOR_LOVED_SONG = 0.1;
    const POINT_FOR_PLAYED_SONG = 0.2;

    /**
     *
     * @var int
     */
    private $_limit;
    /**
     *
     * @var array[LastFM_User]
     */
    private $_users;
    /**
     *
     * @var array[Artist]
     */
    private $_preferedArtists;
    /**
     *
     * @var array[Track]
     */
    private $_preferedSongs;
    /**
     *
     * @var array[string]
     */
    private $_selectedArtists;
    /**
     *
     * @var array[Track]
     */
    private $_playlist;
    /**
     *
     * @var LastFM
     */
    private $_fm;

    /**
     *
     * @var string
     */

    private $_name;

    /**
     * Konstruktor třídy
     *
     * @param
     */
    public function __construct()
    {
        $this->_fm = LastFM::getInstance();
        $this->_playlist = array();
        $this->_preferedArtists = array();
        $this->_preferedSongs = array();
        $this->_selectedArtists = array();
        $this->_users = array();
        $this->_name = 'New Playlist';
    }

    /**
     *
     * @param string $name
     */

    public function setName($name)
    {
        $this->_name = $name;
    }

    /**
     *
     * @return string
     */

    public function getName()
    {
        return $this->_name;
    }

    /**
     * metoda, která dle vybraných kritérií vybere skladby od daných umělců, které budou v playlistu
     */
    private function selectingSongs()
    {
        $userCount = count($this->_users);

        //požadované skladby se přidají do playlistu ASI NEFACHÁ
        foreach ($this->_preferedSongs as $preferedSong)
        {
            $this->_playlist[] = $preferedSong;
        }

        //požadovaní umělci se přidají buď oblíbené písničky od uživatele nebo celkově z LastFM
        if($this->_preferedArtists != null)
        {
            foreach ($this->_preferedArtists as $preferedArtist)
            {
                $topSongName = $this->_fm->getUserTracksByArtist(LastFM_User::getActiveUser()->getName(), $preferedArtist)->track[0]->name;
                if($topSongName == null)
                $topSongName = $this->_fm->getArtistTopTracks($artist)->track[0]->name;
            
                $this->_playlist[] = new Track($topSongName,$preferedArtist);
            }
        }
        $songsFromArtists = array();
        //zjištění skladeb od uživatelů
        foreach ($this->_users as $user)
        {
            //zjištění nejpřehrávanějších skladeb a ohodnocení
            $userTopSongs = $this->_fm->getUserTopTracks($user->getName());
            $userTopSongsCount = count($userTopSongs->track);
            for ($i = 0;$i < $this->_limit;$i++)
            {
                if($i >= $userTopSongsCount)
                    break;
                
                $userTopSong = $userTopSongs->track[$i];
                $artistName = (string)$userTopSong->artist->name;
                $songName = (string)$userTopSong->name;
                $keyValue = $artistName."|".$songName;
                if(key_exists($keyValue, $songsFromArtists))
                {
                    $songsFromArtists[$keyValue] += self::POINT_FOR_TOP_SONG;
                }
                else
                {
                    $songsFromArtists[$keyValue] = self::POINT_FOR_TOP_SONG;
                }
            }

            //zjištění "love tracks" a jejich ohodnocení
            $userLovedSongs = $this->_fm->getUserLovedTracks($user->getName());
            $count = $userLovedSongs->lovedtracks["total"];
            settype($count, "int");
            if($count > 0)
            {
                foreach ($userLovedSongs->track as $userLovedSong)
                {
                    $artistName = (string)$userLovedSong->artist->name;
                    $songName = (string)$userLovedSong->name;
                    $keyValue = $artistName."|".$songName;
                    if(key_exists($keyValue, $songsFromArtists))
                    {
                        $songsFromArtists[$keyValue] += self::PERCENT_POINT_FOR_LOVED_SONG * $userCount;
                    }
                    else
                    {
                        $songsFromArtists[$keyValue] = self::PERCENT_POINT_FOR_LOVED_SONG * $userCount;
                    }
                }
            }   
        }

        //výběr skladeb s nejvyššími bodovými ohodnoceními
        arsort($songsFromArtists);
        $selectedArtists = array();
        foreach ($songsFromArtists as $artistNameAndSong => $songPoints)
        {
            $artistName = substr($artistNameAndSong, 0, strrpos($artistNameAndSong, "|"));
            $songName = substr($artistNameAndSong,strrpos($artistNameAndSong, "|")+1);

            if(!in_array($artistName, $selectedArtists))
            {
                $this->_playlist[] = new Track($songName,$artistName);
                unset($songsFromArtists[$artistNameAndSong]);
                $selectedArtists[] = $artistName;
            }
            if(count($this->_playlist) == $this->_limit)
                break;
        }

        //pokud není playlist plný, doplní se dodatečnými skladbami
        while(count($this->_playlist) < $this->_limit)
        {
            if(count($songsFromArtists) != 0)
            {
                reset($songsFromArtists);
                $artistNameAndSong = key($songsFromArtists);
                $artistName = substr($artistNameAndSong, 0, strrpos($artistNameAndSong, "|"));
                $songName = substr($artistNameAndSong,strrpos($artistNameAndSong, "|")+1);
                $this->_playlist[] = new Track($songName,$artistName);
                unset($songsFromArtists[$artistNameAndSong]);
            }
            else
            {
                break;
            }
        }
        
    }

    /**
     * metoda, která náhodně zamíchá playlist
     */
    private function randomizePlaylist()
    {
        srand();
        shuffle($this->_playlist);
    }

    /**
     *
     * @return array[Artist]
     */
    public function getArtists()
    {
        return $this->_preferedArtists;
    }

    /**
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->_limit;
    }

    /**
     * metoda, generující playlist
     *
     * @return Playlist
     */
    public function getPlaylist()
    {
        $this->selectingSongs();
        $this->randomizePlaylist();

        return new Playlist($this->getName(), $this->_playlist);
    }

    /**
     * metoda, která vrací seznam skladeb, které mají být součástí playlistu
     *
     * @return array[Song]
     */
    public function getTracks()
    {
        return $this->_preferedSongs;
    }

    /**
     * metoda, která slouží k určení umělců (kontrola existence přes LastFM), kteří mají být zahrnuti v playlistu
     *
     * @param array[Artist] $preferedArtists
     */
    public function setArtists($preferedArtists)
    {
        $this->_preferedArtists = $preferedArtists;
    }

    /**
     * metoda, která nastaví nový limit playlistu
     *
     * @param int $newLimit
     */
    public function setLimit($newLimit)
    {
        if (is_int($newLimit) && $newLimit >= self::STANDARD_LIMIT && $newLimit <= self::MAX_LIMIT)
        {
            $this->_limit = $newLimit;
        }
        else
        {
            $this->_limit = self::STANDARD_LIMIT;
        }
    }

    /**
     * metoda, která umožňuje určení skladeb, které budou v playlistu (včetně kontroly přes LastFM)
     *
     * @param array[Track] $preferedSongs
     */
    public function setTracks($preferedSongs)
    {
        foreach ($preferedSongs as $preferedSong)
        {
            $foundedSong = $this->_fm->findSong($preferedSong->getSongName(), $preferedSong->getArtist()->getName());
            if ($foundedSong != null)
            {
                $this->_preferedSongs[] = $foundedSong;
            }
        }
    }

    /**
     * metoda, která nastavuje seznam uživatelů, jenž budou zahrnuti do generování playlistu
     *
     * @param array[LastFM_User] $users
     */
    public function setUsers($users)
    {
        $this->_users = $users;
    }
}
